﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Network.StepProtocol;

public class StepHead
{
    public const ushort FieldID = 0x0001;
    public ushort PackageID;
    public ushort BodyLen;
    public bool MessageChain;
    public int MsgSeqNum;
}
public class StepTail
{
    public const ushort FieldID = 0x0002;
    public ushort CheckSum;
}
public class StepUtility
{
    public const int StepHeadLen = 36;
    public const int StepTailLen = 7;
    public const char SOH = '\u0001';

    public static bool GetNextSoh(byte[] buff, int startIndex, int endIndex, out int sohIndex)
    {
        sohIndex = 0;
        for (int i = startIndex; i < endIndex; ++i)
        {
            if (buff[i] == SOH)
            {
                sohIndex = i;
                return true;
            }
        }
        return false;
    }
    public static bool GetNextEqual(byte[] buff, int startIndex, int endIndex, out int equalIndex)
    {
        equalIndex = startIndex;
        for (int i = startIndex; i < endIndex; ++i)
        {
            if (buff[i] == '=')
            {
                equalIndex = i;
                return true;
            }
        }
        return false;
    }
    public static bool GetNext(byte[] buff, int startIndex, int endIndex, out ushort key, out string value, out int sohIndex)
    {
        key = 0;
        value = "";
        if (!GetNextSoh(buff, startIndex, endIndex, out sohIndex))
        {
            return false;
        }
        if (!GetNextEqual(buff, startIndex, sohIndex, out int equalIndex))
        {
            return false;
        }
        var keyStr = Encoding.UTF8.GetString(buff[startIndex..equalIndex]);
        if (!ushort.TryParse(keyStr, out key))
        {
            return false;
        }
        value = Encoding.UTF8.GetString(buff[(equalIndex + 1)..sohIndex]);
        return true;
    }
    public static bool GetFieldStart(byte[] buff, int startIndex, int endIndex, out ushort fieldID, out int fieldStartIndex)
    {
        fieldID = 0;
        fieldStartIndex = 0;
        for (int i = startIndex; i < endIndex; ++i)
        {
            if (buff[i - 1] == SOH && buff[i] == '6' && buff[i + 1] == '=')
            {
                if (!GetNext(buff, i, endIndex, out ushort key, out string fieldIDStr, out int sohIndex))
                {
                    return false;
                }
                fieldStartIndex = i;
                try
                {
                    fieldID = Convert.ToUInt16(fieldIDStr, 16);
                    return true;
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"GetFieldStart Parse FieldID Failed. Message:{ex.Message}");
                    return false;
                }
            }
        }
        return false;
    }
    public static bool GetFieldEnd(byte[] buff, int startIndex, int endIndex, out ushort fieldID, out int fieldEndIndex)
    {
        fieldID = 0;
        fieldEndIndex = 0;
        for (int i = startIndex; i < endIndex; ++i)
        {
            if (buff[i - 1] == SOH && buff[i] == '7' && buff[i + 1] == '=')
            {
                if (!GetNext(buff, i, endIndex, out ushort key, out string fieldIDStr, out int sohIndex))
                {
                    return false;
                }
                fieldEndIndex = sohIndex + 1;
                try
                {
                    fieldID = Convert.ToUInt16(fieldIDStr, 16);
                    return true;
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"GetFieldEnd Parse FieldID Failed. Message:{ex.Message}");
                    return false;
                }
            }
        }
        return false;
    }
    public static bool GetNextFieldZone(byte[] buff, int startIndex, int endIndex, out ushort fieldID, out int fieldStartIndex, out int fieldEndIndex)
    {
        fieldEndIndex = 0;
        if (!GetFieldStart(buff, startIndex, endIndex, out fieldID, out fieldStartIndex))
        {
            return false;
        }
        if (!GetFieldEnd(buff, startIndex, endIndex, out ushort fieldIDEnd, out fieldEndIndex))
        {
            return false;
        }
        if (fieldID != fieldIDEnd)
        {
            Console.WriteLine($"FiledID not Match: fieldIDStart:{fieldID}, fieldIDEnd:{fieldIDEnd}");
            return false;
        }
        return true;
    }
    public static bool GetPackageStart(byte[] buff, int startIndex, int endIndex, out int packageStartIndex)
    {
        packageStartIndex = startIndex;
        for (int i = startIndex; i < endIndex; ++i)
        {
            if (buff[i] == SOH && buff[i + 1] == '1' && buff[i + 2] == '=')
            {
                packageStartIndex = i;
                return true;
            }
        }
        return false;
    }

    public static void WriteString<T>(StringBuilder s, ushort key, T value)
    {
        s.AppendFormat($"{key}={value}{SOH}");
    }
    public static void WriteHexString(StringBuilder s, ushort key, ushort value)
    {
        s.AppendFormat($"{key}={value:X4}{SOH}");
    }
    public static void HeadToStream(StringBuilder s, StepHead head)
    {
        s.AppendFormat($"{SOH}{StepHeadIDs.PackageID}={head.PackageID:X4}{SOH}");
        s.AppendFormat($"{StepHeadIDs.BodyLen}={head.BodyLen:D5}{SOH}");
        s.AppendFormat($"{StepHeadIDs.MessageChain}={Convert.ToInt16(head.MessageChain):D1}{SOH}");
        s.AppendFormat($"{StepHeadIDs.MsgSeqNum}={head.MsgSeqNum:D13}{SOH}");
    }
    public static bool HeadFromStream(byte[] buff, int startIndex, int endIndex, StepHead head)
    {
        startIndex += 1;
        while (startIndex < endIndex)
        {
            if (GetNext(buff, startIndex, endIndex, out ushort key, out string value, out int sohIndex))
            {
                switch (key)
                {
                    case StepHeadIDs.PackageID:
                        head.PackageID = Convert.ToUInt16(value, 16);
                        break;
                    case StepHeadIDs.BodyLen:
                        head.BodyLen = Convert.ToUInt16(value);
                        break;
                    case StepHeadIDs.MessageChain:
                        head.MessageChain = Convert.ToBoolean(Convert.ToInt16(value));
                        break;
                    case StepHeadIDs.MsgSeqNum:
                        head.MsgSeqNum = Convert.ToInt32(value);
                        break;
                    default:
                        Console.WriteLine($"UnExpected Key:0x{key:X}");
                        return false;
                }
                startIndex = sohIndex + 1;
            }
            else
            {
                Console.WriteLine("HeadFromStream: GetNext Failed.");
                break;
            }
        }
        return true;
    }
    public static void TailToStream(StringBuilder s, StepTail tail)
    {
        s.AppendFormat("{0}={1:D3}{2}\u0000", StepHeadIDs.CheckSum, tail.CheckSum, SOH);
    }
    public static bool TailFromStream(byte[] buff, int startIndex, int endIndex, StepTail tail)
    {
        while (startIndex < endIndex)
        {
            if (GetNext(buff, startIndex, endIndex, out ushort key, out string value, out int sohIndex))
            {
                switch (key)
                {
                    case StepHeadIDs.CheckSum:
                        tail.CheckSum = Convert.ToUInt16(value);
                        break;
                    default:
                        Console.WriteLine($"UnExpected Key:0x{key:X}");
                        return false;
                }
                startIndex = sohIndex + 1;
            }
            else
            {
                break;
            }
        }
        return true;
    }
    public static ushort CalculateSum(byte[] buff, int startIndex, int endIndex)
    {
        int sum = 0;
        for (int i = startIndex; i < endIndex; ++i)
        {
            sum += buff[i];
        }
        return (ushort)(sum % 256);
    }
}
